#include <iostream>

#include "Image.H"
#include "IO.H"
#include "Util.H"

using namespace pip;
using namespace std;

void getHistogram(const Image<unsigned char> &image, vector<int> &vHist)
{
  if (image.empty())
  {
	  cerr << "Trying to calculate histogram of an empty image"
		   << endl;
	  return;
  }
  else
  {
		int iX_max = image.dim(0);
		int iY_max = image.dim(1);
		for(int iX=0; iX < iX_max; ++iX)
		{
			for(int iY=0; iY < iY_max; ++iY)
			{
				vHist[image(iX,iY)]++; //count frequencies
			}
		}
  }
}


void histEqualization(Image<unsigned char> &image)
{
    if (image.empty())
    {
	  cerr << "Trying to calculate histogram of an empty image"
		   << endl;
	  return;
    }
	else
	{
		  vector<int> vHist(256); //histogram
		  vector<int> vCumHist(256); //cummulative histogram
		  vector<float> vLUT(vHist.size(),0); //LookUpTable
		  getHistogram(image, vHist);
		  int iNumPixel = image.dim(0)*image.dim(1); //total number of pixels in image
		  for(int pos = 0; pos < vHist.size(); ++pos)
		  {
				if(pos==0)
				vCumHist[pos]=vHist[pos];       //first entry is the same
				else
				vCumHist[pos]=vHist[pos]+vCumHist[pos-1];    //sum over all entries
				//Hc normed over all pixel => mapped to colour
				vLUT[pos]= vCumHist[pos]*255/(float)iNumPixel; 
		  }
		  for(int iX = 0; iX < image.dim(0); ++iX)
		  {
				for(int iY = 0; iY < image.dim(1); ++iY)
				{	
					image(iX,iY)=vLUT[image(iX,iY)];//assign new values to pixel
				}
		  }
	}
}


void histEqualization2(Image<unsigned char> &image)
{
  if (image.empty()) return;
  vector<pair<unsigned char, pair<int, int> > > v;
  for (int y = 0; y < image.dim(1); ++y) 
  {
	for (int x = 0; x < image.dim(0); ++x) 
	{
		v.push_back(pair<unsigned char, pair<int, int> >(image(x, y), pair<int, int>(x, y)));
	}
  }
  sort(v.begin(), v.end());
  float fSize(v.size());
  for (unsigned int i = 0; i < v.size(); ++i) 
  {
	  image(v[i].second.first, v[i].second.second) = i/fSize*255;
  }
}


void histToImage(const vector<int> &vHist, Image<unsigned char> &histImage)
{
  if (histImage.empty()) 
  {
	  cerr << "Trying to save empty histogram"
		   << endl;
	  return;
  }
  else
  {
	int iH = histImage.dim(1);
	int iMax_value = *max_element(vHist.begin(), vHist.end());
	for (int iX=0; iX < vHist.size(); iX++)//for all colours
	{
		double dTmp = (double)vHist[iX] / (double)iMax_value; //frequency of this colour
		for (int iY=0; iY <= iH; iY++)
		{
			if(iY < (iH - (dTmp*iH)))
			{
				histImage(iX,iY)=0;//background
			}
			else
			{
				histImage(iX,iY)=255;//white line
			}
		}
	}
  }
}


void windowing(Image<unsigned char> &image, const int &l, const int &r)
{
	int iR = r;
	int iL = l;
	if (image.empty() || r <= l ) return;
	if (r>255) iR=255; 
	if (l<0)   iL=0;
	vector<float> vLut(256,0); //lookup table in three loops (could be done in one but too much 'if'
	for(int iPos = 0; iPos < iL; ++iPos)
	{
		vLut[iPos]=0;//from 0 to w1
	}
	for(int iPos = 255; iPos >= iR; --iPos)
	{
		vLut[iPos]=255;//from w2 to 255
	}
	for(int iPos = 0; iPos <= iR-iL; ++iPos)
	{
		vLut[iPos+iL]=(float)iPos/(float)(iR-iL)*(float)255;//rest of the spektrum between w1 w2
	}	
	for(int iX = 0; iX < image.dim(0); ++iX)
	{
		for(int iY = 0; iY < image.dim(1); ++iY)
		{	
			image(iX,iY)=vLut[image(iX,iY)];//assign values
		}
	}
}


int main(int argc, char *argv[])
{
  string sProgram = argv[0];//look for commandline arguments: program name
  if (argc < 2) 
  {
	error(sProgram, "No arguments was supplied. Usage of the program: transformations [image_filename]");
	return 2;
  }
  string filename = argv[1];//look for commandline arguments: target file
  Image<unsigned char> input;
  string basic_filename = filename.substr(0, filename.size() - 4);
  vector<int> vHistogram(256);
  vector<int> vEqHistogram(256);
  const int hist_height = 200;
  string sOption;
  bool bEqu  = false; 
  bool bWin  = false;
  bool bEqu2 = false;
  do        //show menue - read input- set bools
  {
	  cout	<< "\n------------------------------------------------\n" 
			<< "h  -  for histogram equalization\n"
			<< "h2 -  use equalization2 \n"
			<< "w  -  for windowing\n"
			<< "hw -  for hist. equalization & windowing\n"
			<< "------------------------------------------------\n \n";
	  cin >> sOption;
	  if(strcmp(sOption.c_str(),"hw")==0     || strcmp(sOption.c_str(),"HW")==0)
	  {
		  bWin = bEqu = true;break;
	  }
	  else if(strcmp(sOption.c_str(),"h")==0 || strcmp(sOption.c_str(),"H")==0)
	  {
		  bEqu = true;break;
	  }
	  else if(strcmp(sOption.c_str(),"w")==0 || strcmp(sOption.c_str(),"W")==0)
	  {
		  bWin = true;break;
	  }
	  else if(strcmp(sOption.c_str(),"h2")==0 || strcmp(sOption.c_str(),"H2")==0)
	  {
		  bEqu2 = true;break;
	  }
	  else
	  {
		  cout << "unclear Input... \n please try again:";
	  }
  }while(!bWin && !bEqu && !bEqu2);


  importFile(input, filename);
  if (input.empty()) {
	cout << "Image " << filename << " not found" << endl;
	error(sProgram, " is terminated");
	system("pause");
  } else {
	cout << "Image " << filename << " has been read" << endl;
  }


  if(bEqu) //if hist-equalization is choosen
  {
	  cout << "Part 1: histogram equalization" << endl;//create input histogram
	  getHistogram(input, vHistogram); 
	  Image<unsigned char> inputHistImage(Dimension(vHistogram.size(), hist_height)); 
	  histToImage(vHistogram, inputHistImage);
	  exportFile(inputHistImage, "input_histogram.bmp");

	  Image<unsigned char> equalizedImage(input);//do hist-equalization on input image
	  histEqualization(equalizedImage); 
	  exportFile(equalizedImage, basic_filename + "_afterEqualization.bmp");
	 
	  Image<unsigned char> outputHistImage(Dimension(vHistogram.size(), hist_height));//create output histogram
	  getHistogram(equalizedImage, vEqHistogram);
	  histToImage(vEqHistogram, outputHistImage);
	  exportFile(outputHistImage, "output_histogram.bmp");
  }


  if(bWin)   //if windowing
  {
	  int iW1, iW2;
	  Image<unsigned char> outputWinImage(input);
	  cout << "Part 2: windowing of the image" << endl;
	  cout << "Insert left cut-off parameter :";
	  cin >> iW1;
	  cout << "Insert right cut-off parameter :";
	  cin >> iW2;
	  
	  windowing(outputWinImage, iW1, iW2);   //create windowing result and save it
	  exportFile(outputWinImage, basic_filename + "_afterWindowing.bmp");
  }
  

  if(bEqu2)   //if hist-equalization2
  {
	  cout << "Part 3: histogram equalization2" << endl; //create input histogram
	  getHistogram(input, vHistogram); 
	  Image<unsigned char> inputHistImage(Dimension(vHistogram.size(), hist_height)); 
	  histToImage(vHistogram, inputHistImage);
	  exportFile(inputHistImage, "input_histogram_equ2.bmp");

	  Image<unsigned char> equalizedImage(input);//do hist-equalization2 on input image
	  histEqualization2(equalizedImage); 
	  exportFile(equalizedImage, basic_filename + "_afterEqualization2.bmp");
	 
	  Image<unsigned char> outputHistImage(Dimension(vHistogram.size(), hist_height));//create output histogram
	  getHistogram(equalizedImage, vEqHistogram);
	  histToImage(vEqHistogram, outputHistImage);
	  exportFile(outputHistImage, "output_histogram_equ2.bmp");
  }

  system("pause");	
  return 0;
}



/*  how I found Max of the histograms */
//  int m1 = *max_element(vEqHistogram.begin(), vEqHistogram.end());
//  int m2 = *max_element(vHistogram.begin(), vHistogram.end());  
//  for (int i=0;i<256;++i)
//  {	
//		cout << i << "\t\t" << vHistogram[i] << "\t\t" << vEqHistogram[i] << "\t\t" << vEqHistogram[i]-vHistogram[i];
//		if (vHistogram[i]==m2)cout<<"MAXor " ;
//		if (vEqHistogram[i]==m1)cout<<"MAXeq " ;
//		cout << endl;
//  }
//  cout << m1 << "  max  " << m2 << endl;
